import 'dart:async';
import 'dart:convert';
import 'dart:ffi';
import 'dart:math';

import 'package:awesome_notifications/awesome_notifications.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pet/app/models/clients.dart';
import 'package:pet/app/models/shortcut.dart';
import 'package:pet/app/routes/app_pages.dart';
import 'package:pet/app/storage/token.dart';
import 'package:pet/env.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;
import 'dart:io';

class WebsocketController extends GetxController {
  bool is_connect = false;

  late WebSocketChannel channel;

  late var messages = <String, List<types.Message>>{};

  late int memberId;

  late String nickname = '';

  late String avatar = '';

  late String currentId = '';

  late bool breathe = false;

  late bool logining = false;

  int historyPage = 1;

  int closeId = 0;

  List<Shortcut> shortcutsData = [];

  late Map<String, Function> functionMap = {
    'backendSite.token': backendSiteToken,
    'backendSite.login': backendSiteLogin,
    'bubble.index': bubbleIndex,
    'member.close': close,
    'bubble.history': bubbleHistory,
    'conversation.currentList': conversations,
    'conversation.index': conversations,
    'service.message': serviceMessage,
    'bubble.read': bubbleRead,
    'member.message': memberMessage,
    'queueUp.matching': matching,
    'service.shortcut': shortcut,
  };

  bool get isConnect => is_connect;

  late var clients = <String, Clients>{};

  late var historyClients = <String, Clients>{};
  late RefreshController refreshController;
  @override
  void onInit() {
    refreshController = RefreshController(
        initialRefresh: true, initialRefreshStatus: RefreshStatus.canRefresh);
    super.onInit();
    connect();
  }

  @override
  void onReady() {
    super.onReady();
  }

  @override
  void onClose() {
    super.onClose();
    closeWebsocket();
    refreshController.dispose();
  }

  closeWebsocket() {
    channel.sink.close();
  }

  /// 连接
  connect() {
    runZoned(() async {
      channel = WebSocketChannel.connect(Uri.parse(config().wss));
      listen();
    }, onError: (e, stack) {});
  }

  reconnect() async {
    await Future.delayed(const Duration(seconds: 3)); // 等待5秒钟重连
    await connect();
  }

  /// 监听
  listen() {
    channel.stream.listen(
        (event) async {
          is_connect = true;
          update();
          print('----------------event start--------------');
          print(event);
          print('----------------event end--------------');
          var data = jsonDecode(event);
          if (data['code'] == '2001') {
            refreshToken();
          }
          if (data['code'] == '4101') {
            forceLogout();
          }
          callEvent(data);
        },
        onError: (error) {},
        onDone: () async {
          is_connect = false;
          update();
          await reconnect();
        });
  }

  refreshToken() {
    TokenStorage tokenStorage = Get.find<TokenStorage>();

    if (!tokenStorage.getToken()?.isEmpty &&
        !tokenStorage.getUsername()?.isEmpty &&
        !tokenStorage.getPassword()?.isEmpty) {
      getToken(tokenStorage.getUsername(), tokenStorage.getPassword());
    } else {
      print("objectobjectobjectobject");
    }
  }

  /// 心跳检查
  ping() async {
    Timer.periodic(const Duration(seconds: 20), (timer) async {
      channel.sink.add(await package("backendSite.ping"));
    });

    Timer.periodic(const Duration(seconds: 2), (timer) async {
      breathe = !breathe;
      update();
    });
  }

  void callEvent(dynamic data) {
    final Function? event = functionMap[data['route']];
    if (event != null) {
      event(data);
    } else {
      print("${data['route']}: not found");
    }
  }

  backendSiteToken(dynamic data) {
    logining = false;

    if (data['code'] == '422') {
      Get.snackbar('错误', data['data'], colorText: Colors.red);
      update();
      return;
    }
    TokenStorage tokenStorage = Get.find<TokenStorage>();
    tokenStorage.setToken(data['data']);
    // 登录，将fd和token进行绑定
    login();
    update();
  }

  backendSiteLogin(dynamic data) {
    try {
      if (data['code'] == '2000' && data['data'] == '登录成功') {
        memberId = data['member']['id'];
        avatar = data['member']['head_portrait'];
        nickname = data['member']['nickname'];
        Get.offAllNamed(Routes.CLIENTS, arguments: {"autoLogin": true});
        is_connect = true;
        update();
      }
    } catch (e) {
      print(e);
    }
  }

  autoLogin() async {
    TokenStorage tokenStorage = Get.find<TokenStorage>();

    if (!tokenStorage.getToken().isEmpty) {
      await login();
    }
  }

  forceLogout() async {
    TokenStorage tokenStorage = Get.find<TokenStorage>();
    await tokenStorage.deleteToken();
    await Get.offAllNamed(Routes.LOGIN);
    Get.snackbar(
      "下线通知",
      '',
      margin: const EdgeInsets.only(top: 50, left: 10, right: 10),
      colorText: Colors.black,
      backgroundColor: Colors.white,
      messageText: const Text(
        "您的账号已在其他地方登录",
        style: TextStyle(
          fontWeight: FontWeight.bold,
        ),
      ),
    );
  }

  logout() async {
    TokenStorage tokenStorage = Get.find<TokenStorage>();
    await tokenStorage.deleteToken();
    tokenStorage.deleteUsername();
    tokenStorage.deletePassword();
    await Get.offAllNamed(Routes.LOGIN);
  }

  login() async {
    channel.sink.add(await package(
      "backendSite.login",
    ));
    ping();
  }

  /// 通过用户名密码获取token
  getToken(String username, String password) async {
    TokenStorage tokenStorage = Get.find<TokenStorage>();
    tokenStorage.setUsername(username);
    tokenStorage.setPassword(password);
    logining = true;

    // 通过账号和密码获取用户token
    channel.sink.add(await package("backendSite.token", params: {
      "username": username,
      "password": password,
    }));
    update();
  }

  getClients() async {
    channel.sink.add(await package("bubble.index"));
  }

  // 获取历史聊天客户
  getHistoryClients() async {
    channel.sink.add(await package("bubble.history"));
  }

  loadMoreHistoryClients() async {
    channel.sink
        .add(await package("bubble.history", params: {'page': ++historyPage}));
  }

  bubbleIndex(dynamic data) {
    clients.clear();
    for (var client in data['data'] as List) {
      clients[client['id']] = Clients.fromJson(client);
    }

    update();
  }

  bubbleHistory(dynamic data) {
    for (var client in data['data'] as List) {
      historyClients[client['id']] = Clients.fromJson(client);
    }
    refreshController.refreshCompleted();
    refreshController.loadComplete();
    if (data['data'].length == 0) {
      refreshController.loadNoData();
    }
    update();
  }

  close(dynamic data) {
    var id = data['data']['id'];
    closeId = id;
    Get.snackbar("退出通知", '',
        margin: const EdgeInsets.only(top: 50, left: 10, right: 10),
        colorText: Colors.black,
        backgroundColor: Colors.white,
        messageText: Text(
          "${data['data']['send_nickname']} 退出人工客服",
          style: const TextStyle(
            fontWeight: FontWeight.bold,
          ),
        ));
    getClients();
    update();
  }

  closeConversion(String id, String memberId) async {
    channel.sink.add(await package(
      "bubble.close",
      params: {"bubble_id": id, "member_id": memberId, "type": "member"},
    ));

    clients.remove(id);
  }

  matching(dynamic data) {
    getClients();
  }

  getShortcut() async {
    channel.sink.add(await package(
      "service.shortcut",
    ));
  }

  shortcut(dynamic data) {
    shortcutsData = shortcutFromJson(jsonEncode(data['data']['content']));
  }

  /// 获取当前用户对话
  getConversations(String id, String sendId, String conversationId) async {
    currentId = id;
    channel.sink.add(await package(
      "conversation.currentList",
      params: {"bubble_id": id, "conversation_id": conversationId},
    ));
    messagesRead(id, sendId);
  }

  /// 获取用户历史对话
  getHistoryConversations(String id, String sendId) async {
    print('获取用户历史对话');
    try {
      currentId = id;
      channel.sink.add(await package(
        "conversation.index",
        params: {"bubble_id": id, "conversation_id": ""},
      ));
    } catch (e) {
      print(e);
    }
    print('获取用户历史对话');
  }

  /// 获取当前对话
  conversations(dynamic data) async {
    print('---------------start 获取客户会话--------------');

    if (data['data'].length == 0) {
      // 没有会话了
      print("-----------没有会话了----------");
      return;
    }
    var id = data['data'][0]['bubble_id'];
    messages[id] = messages[id] ?? <types.Message>[];
    List<types.Message> newData = [];
    try {
      for (var message in data['data'] as List) {
        var member = message['member'];
        types.User user;

        if (message['user_type'] == 'backend') {
          String id = memberId.toString();
          user = types.User(
            id: id,
          );
        } else {
          user = types.User(
            id: randomString(),
          );
        }

        if (message['type'] == 'text') {
          newData.add(types.TextMessage(
            author: user,
            createdAt: int.parse(message['created_at']) * 1000,
            id: message['id'],
            text: message['content'],
          ));
        } else if (message['type'] == 'image') {
          newData.add(types.ImageMessage(
            author: user,
            createdAt: int.parse(message['created_at']) * 1000,
            height: 30,
            id: message['id'],
            name: "image",
            size: 50,
            uri: message['content'],
            width: 20,
          ));
        }
      }
      messages[id] = newData;
    } catch (e) {
      print(e);
    }
    print('---------------end 获取客户会话--------------');
  }

  /// 收到来自客户的消息
  serviceMessage(dynamic data) {
    if (Platform.isAndroid) {
      AwesomeNotifications().createNotification(
        content: NotificationContent(
            id: data['data']['conversation_id'],
            channelKey: 'basic_channel',
            title: data['member']['nickname'],
            body: data['data']['content']),
      );
    }
    print('------------start 接收到用户消息-------------');
    // {type: "text", content: "你好", fd: "468", user_type: "member", bubble_id: 114, conversation_id: 408}

    var message = data['data'];
    var id = message['bubble_id'].toString();
    try {
      if (messages[id] == null) {
        messages[id] = [];
      }
      types.User user = types.User(
        id: data['member']['member_id'].toString(),
        firstName: data['member']['nickname'],
        imageUrl: data['member']['head_portrait'],
      );

      if (message['type'] == 'image') {
        print('-------------image----------------');
        messages[id]!.insert(
            0,
            types.ImageMessage(
              author: user,
              createdAt: DateTime.now().millisecondsSinceEpoch,
              height: 30,
              id: message['conversation_id'].toString(),
              name: "aaa",
              size: 50,
              uri: message['content'],
              width: 20,
            ));

        print('-------------image----------------');
      } else {
        messages[id]!.insert(
          0,
          types.TextMessage(
            author: user,
            createdAt: DateTime.now().millisecondsSinceEpoch,
            id: message['conversation_id'].toString(),
            text: message['content'],
          ),
        );
      }

      update();
    } catch (e) {
      print(e);
    }
    print('------------end 接收到用户消息-------------');
    try {
      if (currentId == id) {
        // 查看是否需要已读
        messagesRead(id, data['member']['member_id'].toString());
      } else {
        // 被客户列表标记未读数
        // clients[id]?.unread++;
        getClients();
        if (message['type'] == 'image') {
          clients[id]?.latestText = '[图片]';
        } else {
          clients[id]?.latestText = message['content'].toString();
        }
      }
    } catch (e) {
      print(e);
    }
  }

  bubbleRead(dynamic data) async {
    print(data);
  }

  /// 接收来自管理员的消息
  memberMessage(dynamic data) {
    // {type: text, content: hahah, fd: 530, user_type: backend, bubble_id: 122, conversation_id: 413}, member: {id: 462, fd: 530, merchant_id: 0, member_id: 7, job_number: 010, nickname: 高兵兵, realname: , head_portrait: , gender: 0, qq: , type: backend, mobile: 18335908792, unread_num: 0, max_reception_num: 100, now_reception_num: 3, residue_reception_num: 97, ip: 127.0.0.1, status: 1, created_at: 1685595640, updated_at: 1686029320}
    var message = data['data'];
    var id = message['bubble_id'].toString();

    print("接收来自管理员的消息");
    print(data['member']['id']);
    try {
      if (messages[id] == null) {
        messages[id] = [];
      }
      types.User user = types.User(
        id: data['member']['id'].toString(),
        firstName: data['member']['nickname'],
        imageUrl: data['member']['head_portrait'],
      );

      if (message['type'] == 'image') {
        messages[id]!.insert(
          0,
          types.ImageMessage(
            author: user,
            createdAt: DateTime.now().millisecondsSinceEpoch,
            height: 30,
            id: message['conversation_id'].toString(),
            name: "aaa",
            size: 50,
            uri: message['content'],
            width: 20,
          ),
        );
      } else {
        messages[id]!.insert(
          0,
          types.TextMessage(
            author: user,
            createdAt: DateTime.now().millisecondsSinceEpoch,
            id: message['conversation_id'].toString(),
            text: message['content'],
          ),
        );
      }

      update();
    } catch (e) {
      print(e);
    }

    if (currentId == id) {
      // 查看是否需要已读
      messagesRead(id, data['member']['id'].toString());
    } else {}
  }

  messagesRead(String id, String memberId) async {
    channel.sink.add(await package(
      "bubble.read",
      params: {"bubble_id": id, "member_id": memberId, 'type': 'member'},
    ));
  }

  sendMessage(String id, String to, types.TextMessage message) async {
    print('---------------bubble id--------------------');
    print(id);
    channel.sink.add(await package(
      "member.message",
      params: {
        "bubble_id": id,
        "member_id": to,
        "content": message.text,
        'type': message.type.toString().split('.').last
      },
    ));
    update();
  }

  sendImageMessage(String id, String to, types.ImageMessage message) async {
    print('---------------bubble id--------------------');
    print(id);
    channel.sink.add(await package(
      "member.message",
      params: {
        "bubble_id": id,
        "member_id": to,
        "content": message.uri,
        'type': message.type.toString().split('.').last
      },
    ));
    update();
  }

  Future<String> package(String route, {dynamic params = const []}) async {
    TokenStorage tokenStorage = Get.find<TokenStorage>();
    var sendMessage = jsonEncode({
      "route": route,
      "token": tokenStorage.getToken(),
      "params": params,
      "time": DateTime.now().toString(),
    });
    return sendMessage;
  }

  String randomString() {
    final random = Random.secure();
    final values = List<int>.generate(16, (i) => random.nextInt(255));
    return base64UrlEncode(values);
  }

  loadHistory() {
    print("----------------history---------------");
  }
}
